#!/usr/bin/env python
# -*- coding: utf-8 -*-

import cherrypy
from cherrypy.lib.static import serve_file

import json
import datetime

from farado.logger import logger
from farado.ui.renderer import view_renderer
from farado.helpers.cookie_helper import current_session_id
from farado.general_manager_holder import project_manager, permission_manager
from farado.general_manager_holder import meta_item_manager, raw_querier
from farado.helpers.issue_column_helper import IssuesColumnHelper
from farado.editors.issue_editor import IssueEditor
from farado.ui.operation_result import OperationResult
from farado.permission_manager import PermissionFlag
from farado.ui.base_view import BaseView, UiUserRestrictions, DataTableArgs
from farado.ui.base_view import bring_value, get_value, get_int_value
from farado.database.raw_querier import intersect
from farado.helpers.issue_report_helper import IssueReportHelper


class IssuesView(BaseView):

    #--------------------------------------------------------------------------#
    def __init__(self):
        super().__init__()
        self.name = '/issues'

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def index(self, **args):
        user = self.current_user()
        if not user:
            return self.login_and_open(args=args)

        args = bring_value(user.pop_last_action_data(), args)

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)

        message = f"Открытие страницы с перечнем запросов."
        logger.info(f"%-18s | {message}", user.login)

        return view_renderer["issues"].render(
            user=user,
            project_manager=project_manager(),
            operation_result=user.pop_last_action_result(),
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
                is_create_enabled=bool(PermissionFlag.creator <= rights),
                is_delete_enabled=bool(PermissionFlag.deleter <= rights),
            ),
            columns=columns_string(args).split(','),
            column_helper=IssuesColumnHelper(),
            filter_fields=filter_fields(args),
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def issues_data(self, parent_id=None, **args):
        user = self.current_user()
        if not user:
            return '{}'

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)

        # Формирование перечня отфильтрованных и сортированных идентификаторов запросов
        table_args = DataTableArgs(args)
        issues_column_helper = IssuesColumnHelper()
        user.push_long_action_handler(
            handler_caption='/issues/issues_data',
            handler=issues_column_helper,
        )
        filtered_issues_ids = issues_column_helper.issues_ids_by_data_table_args(
            table_args=table_args,
        )

        if parent_id:
            total_issues_ids = raw_querier().issues_ids_by_parent_id(parent_id)
            filtered_issues_ids = intersect(filtered_issues_ids, total_issues_ids)

        sliced_issues_ids = table_args.slice_ids(filtered_issues_ids)

        # Получение полного и отображаемого кол-ва запросов
        total_count = meta_item_manager().issues_count()
        filtered_count = len(filtered_issues_ids)

        # Получение экземпляров запросов
        issues = meta_item_manager().issues_by_ids(sliced_issues_ids)

        # Формирование целевых json данных для отображения в таблице
        data = []
        for issue in issues:
            column_helper = IssuesColumnHelper(issue)
            row = {}
            for column in table_args.columns:
                row.update(column_helper.prepare_column_data(column))
            data.append(row)

        result = {
            "draw": table_args.draw,
            "recordsTotal": total_count,
            "recordsFiltered": filtered_count,
            "data": data,
        }
        return json.dumps(result, indent=2)

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def issue(self, target_issue_id=None, **args):
        user = self.current_user()
        if not user:
            if target_issue_id:
                return self.login_and_open(f'/issue/{target_issue_id}', args)
            return self.login_and_open()

        args = bring_value(user.pop_last_action_data(), args)
        is_reading = bool(len(args) == 0)

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)
        if PermissionFlag.watcher > rights:
            message = f"Нет доступа к запросу #{target_issue_id}."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        operation_result = user.pop_last_action_result()

        if is_reading:
            target_issue = project_manager().issue(target_issue_id)
            if not target_issue:
                message = f"Запрос #{target_issue_id} не найден."
                logger.warn(f"%-18s | {message}", user.login)
                raise cherrypy.HTTPError(404, message)
        else:
            result_code, result_text, target_issue_id = IssueEditor(user).change(
                rights,
                target_issue_id,
                **args,
            )
            user.push_last_action_result(
                OperationResult(
                    caption=result_text,
                    result_code=result_code,
                )
            )

            message = f"изменение запроса #{target_issue_id}: {result_text}."
            if result_code:
                logger.info(f"%-18s | Удачное {message}", user.login)
            else:
                logger.warn(f"%-18s | Неудачное {message}", user.login)
                raise cherrypy.HTTPError(403, result_text)

            # NOTE Конструкция нужна при создании нового issue, когда target_issue_id
            # не известен, но нужно добавить его в результирующий uri в адресной строке
            raise cherrypy.HTTPRedirect(
                cherrypy.url(f'/issue/{target_issue_id}')
            )

        # Получение перечня столбцов для таблицы дочерних запросов
        columns = columns_string(args).split(',')
        if target_issue:
            project = project_manager().project(target_issue.project_id)
            if project:
                columns = project.get_issues_view_settings().split(',')

        message = f"Открытие страницы запроса #{target_issue_id}."
        logger.info(f"%-18s | {message}", user.login)

        return view_renderer["issue"].render(
            user=user,
            target_issue=target_issue,
            project_manager=project_manager(),
            operation_result=operation_result,
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
                is_save_enabled=bool(PermissionFlag.editor <= rights),
                is_create_enabled=bool(PermissionFlag.creator <= rights),
                is_delete_enabled=bool(PermissionFlag.deleter <= rights),
            ),
            columns=columns,
            column_helper=IssuesColumnHelper(),
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def add_issue(self, issue_kind_id, **args):
        user = self.current_user()
        if not user:
            return self.login_and_open(f'/add_issue/{issue_kind_id}', args)

        args = bring_value(user.pop_last_action_data(), args)
        parent_id = get_int_value(args, 'parent_id', None)
        project_id = get_int_value(args, 'project_id', None)

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)
        is_done, result_text, temporary_issue = IssueEditor(user).create_issue(
            rights,
            issue_kind_id,
            parent_id,
            project_id,
        )

        message = f"Открытие страницы создания запроса."
        logger.info(f"%-18s | {message}", user.login)

        return view_renderer["new_issue"].render(
            user=user,
            new_issue=temporary_issue,
            save_result=None,
            project_manager=project_manager(),
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id)
            ),
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def remove_issue(self, target_issue_id):
        user = self.current_user()
        if not user:
            return self.login_and_open(f'/remove_issue/{target_issue_id}')

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)

        result_code, result_text = IssueEditor(user).remove_issue(
            rights,
            target_issue_id,
        )
        user.push_last_action_result(
            OperationResult(
                caption=result_text,
                result_code=result_code,
            )
        )

        message = f"удаление запроса #{target_issue_id}: {result_text}."
        if result_code:
            logger.info(f"%-18s | Удачное {message}", user.login)
        else:
            logger.warn(f"%-18s | Неудачное {message}", user.login)

        raise cherrypy.HTTPRedirect(cherrypy.url(f'/'))

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def upload(self, target_issue_id, **args):
        user = self.current_user()
        if not user:
            return "{}"

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)
        result_code, result_text, reserved = IssueEditor(user).add_file(
            rights,
            target_issue_id,
            **args
        )

        message = f"загрузка вложений для запроса #{target_issue_id}: {result_text}."
        if result_code:
            logger.info(f"%-18s | Удачная {message}", user.login)
        else:
            logger.warn(f"%-18s | Неудачная {message}.", user.login)
        return "{}"

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def file(self, target_issue_id, **args):
        user = self.current_user()
        if not user:
            return self.login_and_open(f'/file/{target_issue_id}', args)

        args = bring_value(user.pop_last_action_data(), args)
        file_id = get_int_value(args, 'file_id', None)
        key = get_value(args, 'key', None)

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)

        target_issue = project_manager().issue(target_issue_id)
        if not target_issue:
            message = f"Запрос #{target_issue_id} не найден."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(404, message)

        if not file_id:
            file_id = key

        file = target_issue.file(file_id)
        if not file:
            message = f"Файл #{file_id} в запросе #{target_issue_id} не найден."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(404, message)

        file_path = project_manager().file_manager.file_path(file)

        message = f"Запрошен файл #{file_id} «{file.caption}»."
        logger.info(f"%-18s | {message}", user.login)

        return serve_file(
            file_path,
            "application/x-download",
            "attachment",
            file.caption)

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def remove_file(self, target_issue_id, file_id=None, key=None):
        user = self.current_user()
        if not user:
            return "{}"

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)
        result_code, result_text, reserved = IssueEditor(user).remove_file(
            rights,
            target_issue_id,
            file_id,
            key,
        )

        message = f"удаление вложения для запроса #{target_issue_id}: {result_text}."
        if result_code:
            logger.info(f"%-18s | Удачное {message}", user.login)
        else:
            logger.warn(f"%-18s | Неудачная {message}", user.login)
        return "{}"

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def add_comment(self, target_issue_id, **args):
        user = self.current_user()
        if not user:
            return self.login_and_open(f'/add_comment/{target_issue_id}', args)

        args = bring_value(user.pop_last_action_data(), args)

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)
        result_code, result_text, comment_id = IssueEditor(user).add_comment(
            rights,
            target_issue_id,
            args,
            user
        )
        user.push_last_action_result(
            OperationResult(
                caption=result_text,
                result_code=result_code,
                tab_name='comments'
            )
        )

        message = f"добавление комментария к запросу #{target_issue_id}: {result_text}."
        if result_code:
            logger.info(f"%-18s | Удачное {message}", user.login)
        else:
            logger.warn(f"%-18s | Неудачная {message}", user.login)

        raise cherrypy.HTTPRedirect(
            cherrypy.url(f'/issue/{target_issue_id}')
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def remove_comment(self, target_issue_id, comment_id):
        user = self.current_user()
        if not user:
            return self.login_and_open(f'/remove_comment/{target_issue_id}/{comment_id}')

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)

        result_code, result_text, _reserved = IssueEditor(user).remove_comment(
            rights,
            target_issue_id,
            comment_id,
        )
        user.push_last_action_result(
            OperationResult(
                caption=result_text,
                result_code=result_code,
                tab_name='comments'
            )
        )

        message = f"удаление комментария запроса #{target_issue_id}: {result_text}."
        if result_code:
            logger.info(f"%-18s | Удачное {message}", user.login)
        else:
            logger.warn(f"%-18s | Неудачная {message}", user.login)

        raise cherrypy.HTTPRedirect(
            cherrypy.url(f'/issue/{target_issue_id}')
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def active_issues(self):
        user = self.current_user()
        if not user:
            return self.login_and_open('/active_issues')

        logger.info(f"%-18s | Получение перечня активных запросов.", user.login)

        return view_renderer["active_issues"].render(
            user=user,
            project_manager=project_manager(),
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id)
            ),
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def report(self, **args):
        user = self.current_user()
        if not user:
            return self.login_and_open('/report', args)

        args = bring_value(user.pop_last_action_data(), args)

        from_date = f'{datetime.datetime.now():%d.%m.%Y}'
        to_date = from_date
        if date_range := get_value(args, 'daterange', None):
            date_range_container = date_range.split(" — ")
            from_date = date_range_container[0]
            to_date = date_range_container[1]

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)

        message = f"Формирование отчёта {from_date} — {to_date}."
        logger.info(f"%-18s | {message}", user.login)

        return view_renderer["issues_report"].render(
            user=user,
            project_manager=project_manager(),
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
            ),
            columns=columns_string(args).split(','),
            column_helper=IssuesColumnHelper(),
            filter_fields=filter_fields(args),
            from_date=from_date,
            to_date=to_date,
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def report_data(self, from_date, to_date, **args):
        user = self.current_user()
        if not user or not from_date or not to_date:
            return '{}'

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)

        # Формирование перечня отфильтрованных и сортированных идентификаторов запросов
        table_args = DataTableArgs(args)
        issues_column_helper = IssuesColumnHelper()
        user.push_long_action_handler(
            handler_caption='/issues/report_data',
            handler=issues_column_helper,
        )
        filtered_issues_ids = issues_column_helper.issues_ids_by_data_table_args(
            table_args=table_args,
        )

        # Формирование перечня запросов подходящих под заданный период
        total_issues_ids = IssueReportHelper().active_issues_ids_in_period(from_date, to_date)
        filtered_issues_ids = intersect(filtered_issues_ids, total_issues_ids)

        sliced_issues_ids = table_args.slice_ids(filtered_issues_ids)

        # Получение полного и отображаемого кол-ва запросов
        total_count = meta_item_manager().issues_count()
        filtered_count = len(filtered_issues_ids)

        # Получение экземпляров запросов
        issues = meta_item_manager().issues_by_ids(sliced_issues_ids)

        # Формирование целевых json данных для отображения в таблице
        data = []
        for issue in issues:
            column_helper = IssuesColumnHelper(issue)
            row = {}
            for column in table_args.columns:
                row.update(column_helper.prepare_column_data(column))
            data.append(row)

        result = {
            "draw": table_args.draw,
            "recordsTotal": total_count,
            "recordsFiltered": filtered_count,
            "data": data,
        }
        return json.dumps(result, indent=2)



#------------------------------------------------------------------------------#
def columns_string(args):
    '''Возвращает перечень столбцов для таблицы с issues из args или по умолчанию
    '''
    if 'columns' in args:
        return args['columns']
    return 'id,kind,state,caption,parent,project'

#------------------------------------------------------------------------------#
def filter_fields(args):
    '''Возвращает словарь наименований полей и значений для фильтрации таблицы
    запросов
    '''
    result = {}
    if 'filter_field' not in args:
        return result

    filter_field = args['filter_field']
    if not type(filter_field) is list:
        filter_field = [filter_field]

    for filter_item in filter_field:
        filter_container = filter_item.split(',')
        if 2 > len(filter_container):
            continue
        result[filter_container[0]] = filter_container[1]
    return result
